# Copyright (c) 2008, Miika Vesti
# All rights reserved.
#
# Redistribution and use in source and binary forms, 
# with or without modification, are permitted provided
# that the following conditions are met:
#
# 1. Redistributions of source code must retain the above
#    copyright notice, this list of conditions and the
#    following disclaimer.
# 2. Redistributions in binary form must reproduce the
#    above copyright notice, this list of conditions
#    and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
# 3. Neither the name of the <NO ORGANIZATION> nor the names
#    of its contributors may be used to endorse or promote
#    products derived from this software without specific
#    prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

import cjson
import hashlib
import string
from urllib import unquote
from xmlrpclib import ServerProxy
from settings import *

TORRENT_FUNCTIONS = {	'info_hash': 'd.get_hash=',
						'name': 'd.get_name=',
						'state': 'd.get_state=',
						'size': 'd.get_size_bytes=',
						'dl': 'd.get_down_rate=',
						'ul': 'd.get_up_rate=',
						'downloaded': 'd.get_down_total=',
						'uploaded': 'd.get_up_total='}

DETAIL_FUNCTIONS = {}

# Map rtorrent integer state values with
# human understandable strings.
STATE_MAP = {	0: 'Stopped',
				1: 'Downloading'}

# Size and transfer speed prefix mapping.
BYTE_PREFIXES = {	0: '',
					1: 'Ki',
					2: 'Mi',
					3: 'Gi',
					4: 'Ti'}

def humanize_bytes(bytes, unit):
	counter = 0
	while bytes > 1024.0:
		bytes = bytes / 1024.0
		counter += 1
	return str(round(bytes, 1)) + ' ' + BYTE_PREFIXES[counter] + unit

def humanize_time(seconds):
	if seconds < 0:
		return ''
	if seconds == 0:
		return '0:00'
	else:
		return str(seconds)

def __validate(torrentsData):
	for t in torrentsData:
		state = t[2]
		size = t[3]
		dl = t[4]
		ul = t[5]
		downloaded = t[6]
		uploaded = t[7]
		done = float(downloaded) / float(size)
		if state == 0 or dl <= 0:
			eta = humanize_time(-1);
		else:
			eta = humanize_time(size / dl)
		if downloaded > 0:
			ratio = str(round(float(uploaded) / float(downloaded), 3))
		else:
			ratio = 'Infinite'
		if t[3] == t[4] and state == 1:
			t[2] = 'Seeding'
			eta = humanize_time(-1);
		else:
			t[2] = STATE_MAP[state]
			done = str(round(100.0 * done, 1)) + ' %'
		t[3] = humanize_bytes(size, 'B')
		t[4] = done
		if not state:
			t[5] = ''
			t[6] = ''
		else:
			t[5] = humanize_bytes(dl, 'B/s')
			t[6] = humanize_bytes(ul, 'B/s')
		t[7] = eta
		t.append(humanize_bytes(downloaded, 'B'))
		t.append(humanize_bytes(uploaded, 'B'))
		t.append(ratio)

# Fetch views, download list and selected torrent
# details from rtorrent.
#
def update(view, info_hash):
	xmlrpc = ServerProxy(XMLRPC_URL)
	resp = {}
	sha1 = hashlib.sha1()
	viewsData = xmlrpc.view_list()
	# Remove excluded views
	viewsData = filter(lambda x: x not in EXCLUDE_VIEWS, viewsData)
	sha1.update(string.join(viewsData, ''))
	viewsKey = sha1.hexdigest()
	resp['views'] = {'key': viewsKey, 'data': viewsData}
	if not view:
		resp['views']['select'] = DEFAULT_VIEW
		view = DEFAULT_VIEW
	torrentsData = xmlrpc.d.multicall(	view,
										'd.get_hash=',
										'd.get_name=',
										'd.get_state=',
										'd.get_size_bytes=',
										'd.get_down_rate=',
										'd.get_up_rate=',
										'd.get_down_total=',
										'd.get_up_total=')
	__validate(torrentsData)
	sha1 = hashlib.sha1()
	for t in torrentsData:
		sha1.update(t[0])
	torrentsKey = sha1.hexdigest()
	resp['torrents'] = {'key': torrentsKey, 'data': torrentsData}
	resp['details'] = ''
	return cjson.encode(resp)

# Handle mod_python request and redirect those
# to update() method.
def index(req):
	args = req.__getattribute__('args')
	#args = None
	if args:
		GET = dict([part.split('=') for part in unquote(str(args)).split('&')])
	else:
		GET = {}
	view = GET.get('view', None)
	info_hash = GET.get('info_hash', None)
	return update(view, info_hash)

